package com.huawei.hwclouds.lubanops.demo.common.client.impl;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.huawei.hwclouds.lubanops.demo.common.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.huawei.hwclouds.lubanops.demo.common.client.AbstractHttpClient;
import com.huawei.hwclouds.lubanops.demo.common.client.HeaderFactory;
import com.huawei.hwclouds.lubanops.demo.common.client.HttpCallResult;
import com.huawei.hwclouds.lubanops.demo.common.constants.Constants;
import com.huawei.hwclouds.lubanops.demo.common.exception.common.InternalErrorException;

/**
 * 采用httpclient对象进行http请求发送的类 <br>
 *
 * @author y00438022
 * @since 2020年2月28日
 */
public abstract class ApacheHttpClientBase extends AbstractHttpClient {
    private static final Logger LOG = LoggerFactory.getLogger(ApacheHttpClientCommon.class);

    public ApacheHttpClientBase(int timeout) {
        super(timeout);

    }

    public ApacheHttpClientBase(int timeout, HeaderFactory headerFactory) {
        super(timeout, headerFactory);
    }

    protected abstract CloseableHttpClient getHttpClient();

    /**
     * 实现http的get请求的发送，如果网络错误等异常会抛出异常， <br>
     *
     * @param url
     * @param params
     * @param headers
     * @param timeout
     * @return
     * @author y00438022
     * @since 2020年3月2日
     */
    @Override
    public HttpCallResult executeSendGet(String url, Map<String, ? extends Object> params, Map<String, String> headers,
        int timeout, String proxyHost, int proxyPort) {

        StringBuilder sb = new StringBuilder();
        boolean isFirst = true;
        if (params != null && !params.isEmpty()) {
            Set<? extends Entry<String, ?>> set = params.entrySet();
            for (Entry<String, ?> entry : set) {
                String key = entry.getKey();
                Object value = entry.getValue();
                String vv;
                try {
                    vv = URLEncoder.encode(value.toString(), Constants.DEFAULT_ENCODING);
                } catch (UnsupportedEncodingException e) {
                    throw new InternalErrorException("UnsupportedEncodingException", e);
                }
                if (isFirst) {
                    isFirst = false;
                    if (!url.contains("?")) {
                        sb.append("?").append(key).append("=").append(vv);
                    } else {
                        sb.append("&").append(key).append("=").append(vv);
                    }
                } else {
                    sb.append("&").append(key).append("=").append(vv);
                }
            }
        }

        url = url + sb.toString();

        HttpGet httpget = new HttpGet(url);

        return sendHttpRequestCommon(httpget, headers, timeout, proxyHost, proxyPort);
    }

    /**
     * 实现http的delete请求的发送，如果网络错误等异常会抛出异常，
     *
     * @param url
     * @param headers
     * @param timeout
     * @return
     */
    public HttpCallResult executeSendDelete(String url, Map<String, String> headers, int timeout, String proxyHost,
        int proxyPort) {
        HttpDelete httpDelete = new HttpDelete(url);

        return sendHttpRequestCommon(httpDelete, headers, timeout, proxyHost, proxyPort);
    }

    /**
     * 直接将内容放在post体里面
     *
     * @param url     发送的地址
     * @param content 发送的内容，json格式
     * @param timeout 超时时间
     * @return http调用结果
     */
    @Override
    public HttpCallResult executeSendPostJSON(String url, Map<String, String> headers, String content, int timeout,
        String proxyHost, int proxyPort) {
        content = StringUtil.clearString(content);
        HttpPost httpPost = new HttpPost(url);
        // 设置请求实体
        HttpEntity entity = new StringEntity(content, Constants.DEFAULT_CHARSET);
        httpPost.setEntity(entity);

        return sendHttpRequestCommon(httpPost, headers, timeout, proxyHost, proxyPort);
    }

    /**
     * 直接将内容放在put体里面
     *
     * @param url     发送的地址
     * @param content 发送的内容，json格式
     * @param timeout 超时时间
     * @return http调用结果
     */
    @Override
    public HttpCallResult executeSendPutJSON(String url, Map<String, String> headers, String content, int timeout,
        String proxyHost, int proxyPort) {
        HttpPut httpPut = new HttpPut(url);
        //设置请求实体
        HttpEntity entity = new StringEntity(content, Constants.DEFAULT_CHARSET);
        httpPut.setEntity(entity);

        return sendHttpRequestCommon(httpPut, headers, timeout, proxyHost, proxyPort);
    }

    /**
     * 封装了发送http请求的基础类
     *
     * @param httpRequest
     * @return
     */
    public HttpCallResult sendHttpRequestCommon(HttpRequestBase httpRequest, Map<String, String> headers, int timeout,
        String proxyHost, int proxyPort) {
        //设置请求和传输超时时间
        RequestConfig.Builder builder = RequestConfig.custom()
            .setSocketTimeout(timeout)
            .setConnectionRequestTimeout(timeout)
            .setConnectTimeout(timeout);

        if (StringUtils.isNotBlank(proxyHost)) {
            HttpHost proxy = new HttpHost(proxyHost, proxyPort, "http");
            builder.setProxy(proxy);
        }

        RequestConfig requestConfig = builder.build();
        httpRequest.setConfig(requestConfig);

        //添加请求头
        httpRequest.addHeader("Content-type", Constants.CONTENT_TYPE_APP_JSON);
        addHeaders(httpRequest, headers);

        HttpEntity entity = null;
        CloseableHttpResponse httpResponse = null;

        try {
            CloseableHttpClient httpclient = getHttpClient();
            httpResponse = httpclient.execute(httpRequest);

            StatusLine st = httpResponse.getStatusLine();
            entity = httpResponse.getEntity();

            String content = "";
            if (entity != null) {
                content = EntityUtils.toString(entity, Constants.DEFAULT_ENCODING);
            }

            Map<String, String> headerMap = populateHeader(httpResponse);

            return new HttpCallResult(st.getStatusCode(), content, headerMap);
        } catch (ClientProtocolException e) {
            throw new InternalErrorException("ClientProtocolException", e);
        } catch (IOException e) {
            throw new InternalErrorException("IOException", e);
        } finally {
            consumeEntity(entity);
            closeResponse(httpResponse);
        }
    }

    /**
     * 增加http请求的header信息
     *
     * @param httpRequestBase
     * @param headers
     */
    private void addHeaders(HttpRequestBase httpRequestBase, Map<String, String> headers) {
        if (headers == null || headers.isEmpty()) {
            return;
        }
        Set<Entry<String, String>> headerEntries = headers.entrySet();
        for (Entry<String, String> entry : headerEntries) {
            httpRequestBase.addHeader(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 将header信息放置在头部
     *
     * @param httpResponse
     * @return
     */
    private Map<String, String> populateHeader(CloseableHttpResponse httpResponse) {
        Map<String, String> resultMap = new HashMap<String, String>();
        Header[] headers = httpResponse.getAllHeaders();
        if (headers != null) {
            for (Header h : headers) {
                resultMap.put(h.getName(), h.getValue());
            }
        }
        return resultMap;
    }

    private void consumeEntity(HttpEntity entity) {
        if (entity != null) {
            try {
                EntityUtils.consume(entity);
            } catch (IOException e) {
                LOG.error("failed to consume entity", e);
            }
        }
    }

    private void closeResponse(CloseableHttpResponse httpResponse) {
        if (httpResponse != null) {
            try {
                httpResponse.close();
            } catch (IOException e) {
                LOG.error("failed to consume httpResponse", e);
            }
        }
    }

}
